#include "IIC.h"
#include "EXTI.h"
#include "string.h"

uint8_t sI2C_State = sI2C_STATE_NA;
uint8_t sI2C_ShiftCounter = 0;
uint8_t sI2C_SlaveAddress = 0;
uint8_t sI2C_ReceiveData  = 0;
uint8_t sI2C_TransmitData = 0x50;

uint8_t sI2C_TransmitBuffer[16]={0x01,0x12,0x23,0x34,0x45,0x56,0x67,0x78,0x89,0x9A,0xAB,0xBC,0xCD,0xDE,0xEF,0xF0};
uint8_t sI2C_TransmitIndex = 0;
uint8_t sI2C_TransmitReceiveDataBuffer[20]={0};
uint8_t sI2C_TransmitRecevieDataCount=0;

/**
  * @brief  IIC初始化
  * @param  None
  * @retval None
  */
void IIC_Config(void)
{
  Exti_Init();
}

/**
  * @brief  IIC_SDA_Pin 引脚方向控制
  * @param  direction: 值为非零时配置IIC_SDA引脚为输入模式 , 值为0时配置IIC_SDA引脚为输出模式
  * @retval None
  */
void sI2C_DIR_SDA(uint8_t direction)
{
  if(direction)
  {
    // GPIO_Init(sI2C_SDA_GPIO, sI2C_SDA_PIN, GPIO_MODE_IN |GPIO_OTYPE_OD);
    GPIOB->CFGMSK = ~(1<<0);
    GPIOB->MODER  = 0x00 * 0x55555555;
    GPIOB->OTYPER = 0x01 * 0xffffffff;
  }
  else
  {
//    /* 配置为开漏输出，必须要接上拉电阻。在开漏模式时，对输入数据寄存器的读访问可得到I/O状态 */
    // GPIO_Init(sI2C_SDA_GPIO, sI2C_SDA_PIN, GPIO_MODE_OUT |GPIO_OTYPE_OD|GPIO_PUPD_UP);
    GPIOB->CFGMSK = ~(1<<0);
    GPIOB->MODER  = 0x01 * 0x55555555;
    GPIOB->OTYPER = 0x01 * 0xffffffff;
    GPIOB->PUPDR  = 0x01 * 0x55555555;
  }
}
/**
  * @brief  当SCL触发上升沿外部中断时处理
  * @param  None
  * @retval None
  */
void sI2C_SCL_RiseHandler(void) /* SCL为上升沿,数据锁定,主从机从SDA总线上获取数据位 */
{
  switch(sI2C_State)
  {
    case sI2C_STATE_ADD:
    {
      /* IIC发送遵义MSB, 先发送高位,在发送低位,所以在接收的时候,数据进行左移 */
      sI2C_SlaveAddress <<= 1;
      sI2C_ShiftCounter += 1;
      
      if(sI2C_GET_SDA == Bit_SET)
      {
        sI2C_SlaveAddress |= 0x01;
      }
      /* 当接收到8位地址位后,从机需要在第9个时钟给出ACK应答,等待SCL下降沿的收给出ACK信号 */
      if(sI2C_ShiftCounter == 8)
      {
        // 从设备地址判断 
        if((sI2C_SlaveAddress&0xFE)==SLAVEID)
        {
          if(sI2C_SlaveAddress & 0x01)//主机读
          {

          }
          else  //主机写
          {
            sI2C_TransmitRecevieDataCount=0;
            memset(sI2C_TransmitReceiveDataBuffer,0,20);//清空接收缓冲
          }
          sI2C_State=sI2C_STATE_ADD_ACK;  //设备地址比对正确, 状态进入下一步
        }
        else
        {
          sI2C_State = sI2C_STATE_NA;  //地址比对不正确, 状态恢复
        }        
      }      
    }break;
    case sI2C_STATE_ADD_ACK:
    {
      sI2C_State = sI2C_STATE_DAT;  /* 从机地址的ACK回复后,切换到收发数据状态 */

      sI2C_ShiftCounter = 0;    /* 数据移位计数器清零 */ 
      sI2C_ReceiveData = 0;     /* sI2C的接收数据清零 */ 
      
    }break;
    case sI2C_STATE_DAT:
    {
      if((sI2C_SlaveAddress & 0x01) == 0x00)
      {
        /* 主机写操作:此时从机应该获取主机发送的SDA信号线电平状态,进行为存储 */
        sI2C_ReceiveData <<= 1;
        sI2C_ShiftCounter += 1;
        if(sI2C_GET_SDA == Bit_SET)
        {
          sI2C_ReceiveData |= 0x01;
        }
        /* 当收到一个完整的8位数据时,将收到的数据存放到I2C接收消息队列中, 状态转换给主机发送ACK应答*/
        if(sI2C_ShiftCounter == 8 )
        {
          //将数据压入队列
          if(sI2C_TransmitRecevieDataCount<20)
          {
            sI2C_TransmitReceiveDataBuffer[sI2C_TransmitRecevieDataCount++]=sI2C_ReceiveData;
          }          
          sI2C_ShiftCounter = 0; /* 数据移位计数器清零 */ 
          sI2C_ReceiveData = 0;  /* sI2C的接收数据清零 */ 
          sI2C_State = sI2C_STATE_DAT_ACK;
        }
      }
      else
      {
        /* 主机读操作:在SCL上升沿的时候,主机获取当前SDA的状态位,如果到了第8个数位的上升沿, */
        /*  那接下来就是主机回复从机的应答或非应答信号了,所以将状态切换到等待ACK的状态,同时准备下一个需要发送的数据*/
        
        if(sI2C_ShiftCounter == 8)
        {
          sI2C_ShiftCounter = 0;  /* sI2C的接收数据清零 */ 
          sI2C_TransmitData = sI2C_TransmitBuffer[sI2C_TransmitIndex++];
          sI2C_TransmitIndex %= 16;
          sI2C_State = sI2C_STATE_DAT_ACK;       
        }
      }

    }break;
    case sI2C_STATE_DAT_ACK:
    {
      if((sI2C_SlaveAddress & 0x01) == 0x00)
      {
        /* 主机写操作:从机发送ACK,等待主机读取从机发送的ACK信号 */
        sI2C_State = sI2C_STATE_DAT;   /* 状态切换到数据接收状态 */
      }
      else
      {
        /* 主机读操作: 主机发送ACK,从机可以读取主机发送的ACK信号 */
        if(sI2C_GET_SDA == Bit_RESET)
        {
          sI2C_State = sI2C_STATE_DAT;  /* 接收到ACK,继续发送数据 */
        }
        else
        {
          sI2C_State = sI2C_STATE_STO;  /* 接收到NACK, 停止发送数据 */
        }
      }
    }break;
    default :
    {
      break;
    }
  }
}

/**
  * @brief  当SCL触发下降沿外部中断时的处理
  * @param  None
  * @retval None
  */
void sI2C_SCL_FallHandler(void)   /* SCL为下降沿,数据可变 */
{
  switch (sI2C_State)
  {
    case sI2C_STATE_STA:
    {
      /* 
       *检测到START信号后,SCL第一个下降沿表示开始传输Slave Address,
       *根据数据有效性的规则,地址的第一位需要等到SCL变为高电平时才可以读取
       *切换到获取Slave Address的状态,等待SCL的上升沿触发
      */
      sI2C_State = sI2C_STATE_ADD;
      sI2C_ShiftCounter = 0;   /* 数据移位计数器清零 */
      sI2C_SlaveAddress = 0;   /* sI2C的从机地址清零 */
      sI2C_ReceiveData = 0;    /* sI2C的接收数据清零 */
    }break;
    case sI2C_STATE_ADD:
    {
      /* 在主机发送Slave Address的时候,从机只是读取SDA状态,进行地址解析,所以这边没有处理 */

    }break;
    case sI2C_STATE_ADD_ACK:
    {
      /* SCL在低电平的时候,给I2C总线发送地址的应答信号,状态不发生改变,等待下一个上升沿将ACK发送出去 */
      sI2C_SET_SDA (0);  /* 将SDA信号拉低,向主机发送ACK信号 */

    }break;
    case sI2C_STATE_DAT:
    {
      /* 在SCL时钟信号的下降沿,SDA信号线处理可变的状态 */
      if((sI2C_SlaveAddress & 0x01 ) == 0x00)
      {
        /* 主机写操作:将SDA信号线设置成获取状态,等待下一个SCL上升沿获取数据位 */
        sI2C_DIR_SDA(SDA_IN);
      }
      else
      {
        /* 主机读操作:根据发送的数据位设置SDA信号线的输出电平,等待下一个SCL上升沿 时发送数据位*/
        if(sI2C_TransmitData & 0x80) 
        {
          sI2C_SET_SDA(1);
        }
        else 
        {
          sI2C_SET_SDA (0);
        }
        sI2C_TransmitData <<= 1;
        sI2C_ShiftCounter += 1;
      }
    }break;
    case sI2C_STATE_DAT_ACK:
    {
      /* 在第8个SCL时钟信号下降沿的处理 */
      if((sI2C_SlaveAddress & 0x01) == 0x00)
      {
        /* 主机写操作:从机在接收到数据后,需要给主机一个ACK应答信号,状态不发生改变 */
        sI2C_SET_SDA (0);  /* 将SDA信号拉低,向主机发送ACK信号 */
      }
      else
      {
        /* 主机读操作:从机需要释放当前的SDA信号线,以便主机发送ACK或NACK给从机, 状态不发生改变,等待下一个上升沿读取ACK信号*/
        sI2C_DIR_SDA(SDA_IN);
      }

    }break;
    
    default:
      break;
  }
}

/**
  * @brief  当SDA触发上升沿外部中断时的处理
  * @param  None
  * @retval None
  */
void sI2C_SDA_RiseHandler(void)
{
  if(sI2C_GET_SCL == Bit_SET)  /* SCL为高时,SDA为上升沿:STOP */
  {
    sI2C_State = sI2C_STATE_STO;
  }
  // else       /* SCL为低时,SDA为上升沿:数据的变化 */
  // {

  // }
}
/**
  * @brief  当SDA触发下降沿外部中断时的处理
  * @param  None
  * @retval None
  */
void sI2C_SDA_FallHandler(void)
{
  if(sI2C_GET_SCL == Bit_SET) /* SCL为高时,SDA为下降沿:START */
  {
    sI2C_State = sI2C_STATE_STA;
  }
  // else    /* SCL为低时,SDA为下降沿:数据的变化 */
  // {

  // }
}

/**
  * @brief  This function handles External line 0 interrupt request.
  * @param  None
  * @return None
  */
void EXTI0_IRQHandler(void)/* I2C_SDA */
{
  if(EXTI_GetITStatus(EXTI_Line0) != RESET)
  {
    if(sI2C_GET_SDA == Bit_SET)
    {
      sI2C_SDA_RiseHandler();
    }
    else
    {
      sI2C_SDA_FallHandler();
    }

    /* Clear the  EXTI line 0 pending bit */
    EXTI_ClearITPendingBit(EXTI_Line0);
  }
}

/**
  * @brief  This function handles External lines 1 interrupt request.
  * @param  None
  * @return None
  */
void EXTI1_IRQHandler(void)/* I2C_SCL*/
{
  if(EXTI_GetITStatus(EXTI_Line1) != RESET)
  {
    if(sI2C_GET_SCL == Bit_SET)
    {
      sI2C_SCL_RiseHandler();      
    }
    else
    {
      sI2C_SCL_FallHandler();      
    }

    /* Clear the EXTI line 1 pending bit */
    EXTI_ClearITPendingBit(EXTI_Line1);
  }
}


